#define _REENTRANT
#include<pthread.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/wait.h>
#include"decomposition.h"
#include"commandes_internes.h"
#include"remplacer_fragment.h"
#include"jobs.h"


void invite() // Affiche l'invite de commande.
{
    printf("bash-%s:%s$ ",user,str_replace(home,"~",pwd));
    fflush(stdout);
}

void sigchld()
{
    // Un signal peut correspondre à plusieurs fils finis, donc on boucle.
    while (1)
    {
        int status;
        pid_t pid = waitpid(-1,&status,WNOHANG);
        if (pid<0)
        {
            if (errno==EINTR) continue; // Interrompu => on recommence.
            if (errno==ECHILD) break;   // Plus de fils terminé => on quitte.
            printf("Erreur de wait (%s).\n", strerror(errno));
            break;
        }

        if (pid==0) break; // Plus de fils terminé => on quitte.

        if (WIFEXITED(status))
            afficher_fin_tache(pid);
            supprimer_tache(pid);
        if (WIFSIGNALED(status))
            afficher_fin_tache(pid);
    }
}

void execute()
{
    if (strcmp(mots[0][0], "quit") == 0) // Traitement de la commande quit.
    {
        kill(0,15); // Termine le Shell.
    }
    else if (strcmp(mots[0][0], "cd") == 0) // Traitement de la commande cd.
    {
        interne_cd();
    }
    else if (strcmp(mots[0][0], "jobs") == 0) // Traitement de la commande jobs.
    {
        interne_jobs();
    }
    else
    {
        int status, pid, k, i, tube[nb_commandes-1][2];
        for(i=0; i<nb_commandes-1; i++) // Créations des tubes.
        {
            if(pipe(tube[i])==-1)
            {
                perror("Erreur de création de pipe");
                exit(1);
            }
        }
        for(k=0; k<nb_commandes; k++) // Exécution en boucle des commandes.
        {
            if ((pid=fork())==-1)
            {
                perror("Erreur fork");
                exit(1);
            }
            if (arriere_plan)
            {
                ajouter_tache(pid,mots[0][0]);
            }
            if (pid==0) // Code du kième fils.
            {
                signal(2, SIG_DFL); // Réactive le Ctrl-C pendant l'exécution du fils.
                chdir(pwd);
                if (arriere_plan) // Si la commande doit être lancée en arrière plan.
                {
                    int devnull = open("/dev/null",O_RDONLY);
                    if (devnull != -1)
                    {
                        close(0);
                        dup2(devnull,0);
                    }
                }
                if (redirection=='<' && k==0) // S'il y a une redirection de l'entrée standard (première commande seulement).
                {
                    int fd;
                    if((fd=open(fichier_redirection, O_RDWR|O_CREAT|O_TRUNC,0644))==-1)
                    {
                        perror("Erreur d'ouverture du fichier");
                        exit(1);
                    }
                    close(0);
                    if(dup(fd)<0)
                    {
                        perror("Erreur de duplication");
                        exit(1);
                    }
                    close(fd);
                }
                else if (redirection=='>' && k==nb_commandes-1) // S'il y a une redirection de la sortie standard (dernière commande seulement).
                {
                    int fd;
                    if((fd=open(fichier_redirection, O_RDWR|O_CREAT|O_TRUNC,0644))==-1)
                    {
                        perror("Erreur d'ouverture du fichier");
                        exit(1);
                    }
                    close(1);
                    if(dup(fd)<0)
                    {
                        perror("Erreur de duplication");
                        exit(1);
                    }
                    close(fd);
                }
                if (k==0 && nb_commandes>1) // Redirection de la sortie standard de la première commande.
                {
                    close(1);
                    dup(tube[0][1]);
                }
                else if (k==nb_commandes-1 && nb_commandes>1) // Redirection de l'entrée standard de la dernière commande.
                {
                    close(0);
                    dup(tube[nb_commandes-2][0]);
                }
                else if (nb_commandes>1) // Redirections des autres entrées et sorties standards.
                {
                    close(0);
                    dup(tube[k-1][0]);
                    close(1);
                    dup(tube[k][1]);
                }
                for(i=0; i<nb_commandes-1; i++) // Fermeture des descripteurs inutilisés.
                {
                    close(tube[i][0]);
                    close(tube[i][1]);
                }
                execvp(mots[k][0], mots[k]); // Exécution de la kième commande.
                perror(mots[k][0]);

                exit(0);
            }
        }

        // Code du père.
        for(i=0; i<nb_commandes-1; i++) // Fermeture des descripteurs inutilisés.
        {
            close(tube[i][0]);
            close(tube[i][1]);
        }
        for(k=0; k<nb_commandes; k++) // Attente de la terminaison des fils.
        {
            if (!arriere_plan) //Si le processus fils n'est pas en arrière-plan, le processus père attend sa terminaison
            {
                wait(&status);
            }
        }
    }
}


int main(void)
{
    char* temp;
    temp=getenv("PWD");//Récupère la variable d'environnement PWD
    strcat(pwd, temp);
    temp=getenv("USER");//Récupère la variable d'environnement USER
    strcat(user, temp);
    temp=getenv("HOME");//Récupère la variable d'environnement HOME
    strcat(home, temp);

    while (1)
    {
        signal(2,SIG_IGN); // Désactive le Ctrl-C.
        invite();
        decomposition();
        if (arriere_plan)
        {
            signal(SIGCHLD,sigchld); //Traitement du signal SIGCHLD.
        }
        if (mots[0][0]!=NULL) // L'utilisateur doit entrer une commande.
        {
            execute();
        }
    }
    return(0);
}